package com.togo.algorithm.medium.fsm;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author taiyn
 * @Description 请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。
 * <p>
 * 数值（按顺序）可以分成以下几个部分：
 * <p>
 * 若干空格
 * 一个 小数 或者 整数
 * （可选）一个 'e' 或 'E' ，后面跟着一个 整数
 * 若干空格
 * 小数（按顺序）可以分成以下几个部分：
 * <p>
 * （可选）一个符号字符（'+' 或 '-'）
 * 下述格式之一：
 * 至少一位数字，后面跟着一个点 '.'
 * 至少一位数字，后面跟着一个点 '.' ，后面再跟着至少一位数字
 * 一个点 '.' ，后面跟着至少一位数字
 * 整数（按顺序）可以分成以下几个部分：
 * <p>
 * （可选）一个符号字符（'+' 或 '-'）
 * 至少一位数字
 * 部分数值列举如下：
 * <p>
 * ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
 * 部分非数值列举如下：
 * <p>
 * ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
 * <p>
 * 示例 1：
 * <p>
 * 输入：s = "0"
 * 输出：true
 * 示例 2：
 * <p>
 * 输入：s = "e"
 * 输出：false
 * 示例 3：
 * <p>
 * 输入：s = "."
 * 输出：false
 * 示例 4：
 * <p>
 * 输入：s = "    .1  "
 * 输出：true
 * <p>
 * 提示：
 * <p>
 * 1 <= s.length <= 20
 * s 仅含英文字母（大写和小写），数字（0-9），加号 '+' ，减号 '-' ，空格 ' ' 或者点 '.' 。
 **/
public class Offer20 {

    public boolean isNumber(String s) {

        if (s == null || s.length() == 0)
            return false;

        int index = 0;
        // 去除前面的空格
        while (index < s.length() && s.charAt(index) == ' ')
            index++;

        // 判断符号
        if (index < s.length() && (s.charAt(index) == '+' || s.charAt(index) == '-'))
            index++;

        boolean isNum = false;
        // 判断数字
        while (index < s.length() && s.charAt(index) >= '0' && s.charAt(index) <= '9') {
            index++;
            isNum = true;
        }

        // 判断小数点
        if (index < s.length() && s.charAt(index) == '.') {
            index++;
            // 判断小数点后的数字
            while (index < s.length() && s.charAt(index) >= '0' && s.charAt(index) <= '9') {
                index++;
                isNum = true;
            }
        }

        // 判断e
        if (index < s.length() && (s.charAt(index) == 'e' || s.charAt(index) == 'E') && isNum) {
            index++;
            isNum = false;
            // 判断e后的符号
            if (index < s.length() && (s.charAt(index) == '+' || s.charAt(index) == '-'))
                index++;

            // 判断e后的数字
            while (index < s.length() && s.charAt(index) >= '0' && s.charAt(index) <= '9') {
                index++;
                isNum = true;
            }
        }

        // 去除后面的空格
        while (index < s.length() && s.charAt(index) == ' ')
            index++;

        return isNum && index == s.length();
    }

    /**
     * 状态机解法
     * 按照字符串从左到右的顺序，定义以下 9 种状态。
     * 0 开始的空格
     * 1 幂符号前的正负号
     * 2 小数点前的数字
     * 3 小数点、小数点后的数字
     * 4 当小数点前为空格时，小数点、小数点后的数字
     * 5 幂符号
     * 6 幂符号后的正负号
     * 7 幂符号后的数字
     * 8 结尾的空格
     *
     * @param s
     * @return
     */
    public boolean isNumber_1(String s) {

        if (s == null || s.length() == 0)
            return false;

        Map[] state = {
                // 0
                new HashMap<Character, Integer>() {{
                    put(' ', 0);
                    put('s', 1);
                    put('d', 2);
                    put('.', 4);
                }},
                // 1
                new HashMap<Character, Integer>() {{
                    put('d', 2);
                    put('.', 4);
                }},
                // 2
                new HashMap<Character, Integer>() {{
                    put('d', 2);
                    put('.', 3);
                    put('e', 5);
                    put(' ', 8);
                }},
                // 3
                new HashMap<Character, Integer>() {{
                    put('d', 3);
                    put('e', 5);
                    put(' ', 8);
                }},
                // 4
                new HashMap<Character, Integer>() {{
                    put('d', 3);
                }},
                //5
                new HashMap(){{
                    put('s', 6);
                    put('d', 7);
                }},
                //6
                new HashMap(){{
                    put('d', 7);
                }},
                //7
                new HashMap(){{
                    put('d', 7);
                    put(' ', 8);
                }},
                new HashMap(){{
                    put(' ', 8);
                }}
        };

        int p = 0;
        char t = ' ';
        for (char c : s.toCharArray()) {
            if (c >= '0' && c <= '9') t = 'd';
            else if (c == ' ') t = ' ';
            else if (c == '+' || c == '-') t = 's';
            else if (c == '.') t = '.';
            else if (c == 'e' || c == 'E') t = 'e';
            Map map = state[p];
            Object o = map.get(t);
            if(o == null) return false;
            p = (int) o;
        }

        return p == 2 || p == 3 || p == 7 || p == 8;
    }

    public static void main(String[] args) {
        Offer20 offer20 = new Offer20();
        System.out.println(offer20.isNumber("0"));
    }
}
